package com.example.auth.controller;

import com.example.auth.annotation.RequireRole;
import com.example.auth.dto.ProjectDTO;
import com.example.auth.dto.TaskDTO;
import com.example.auth.entity.User;
import com.example.auth.service.ProjectService;
import com.example.auth.service.TaskService;
import com.example.auth.service.UserService;
import com.example.auth.util.JwtUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProjectService projectService;

    @Value("${app.upload.dir:uploads}")
    private String uploadDir;

    @PostMapping("/avatar")
    public ResponseEntity<?> uploadAvatar(@RequestParam("file") MultipartFile file,
                                          @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7); // Remove "Bearer " prefix
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            // Ensure the upload directory exists
            String directory = new File(uploadDir).getAbsolutePath();
            Path uploadPath = Paths.get(directory);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }

            // Generate a unique filename
            String originalFilename = file.getOriginalFilename();
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String filename = UUID.randomUUID().toString() + extension;
            Path filePath = uploadPath.resolve(filename);

            // Save the file
            Files.copy(file.getInputStream(), filePath);

            // Update the user's avatar URL
            String avatarUrl = "/uploads/" + filename;

            // Call UserService to save the avatar URL
            userService.updateAvatar(user, avatarUrl);

            Map<String, Object> response = new HashMap<>();
            response.put("url", avatarUrl);
            response.put("message", "Avatar uploaded successfully"); // Avatar uploaded successfully

            return ResponseEntity.ok(response);
        } catch (IOException e) {
            return ResponseEntity.badRequest().body(Map.of("message", "Avatar upload failed: " + e.getMessage())); // Avatar upload failed
        }
    }

    @PutMapping("/profile")
    public ResponseEntity<?> updateProfile(@RequestBody Map<String, String> profileData,
                                           @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            // Call the service layer to save personal information
            User updatedUser = userService.updateProfile(user, profileData);

            // Build the response data
            Map<String, Object> response = new HashMap<>();
            Map<String, Object> userData = new HashMap<>();
            userData.put("id", updatedUser.getId());
            userData.put("username", updatedUser.getUsername());
            userData.put("realName", updatedUser.getRealName());
            userData.put("nickname", updatedUser.getNickname());
            userData.put("email", updatedUser.getEmail());
            userData.put("phone", updatedUser.getPhone());
            userData.put("bio", updatedUser.getBio());
            userData.put("avatar", updatedUser.getAvatar());
            userData.put("roles", updatedUser.getRoles().stream().map(role -> role.getName()).toList());

            response.put("user", userData);
            response.put("message", "Personal information updated successfully"); // Personal information updated successfully

            return ResponseEntity.ok(response);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to update personal information: " + e.getMessage())); // Failed to update personal information
        }
    }

    // ==================== Task-related interfaces ====================

    /**
     * Get all tasks assigned to the current user
     */
    @GetMapping("/tasks")
    @RequireRole("USER")
    public ResponseEntity<?> getMyTasks(@RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view their own tasks, User ID: {}", user.getId());

            // Get the tasks assigned to the current user
            List<TaskDTO> tasks = taskService.getTasksByAssignee(user.getId());
            return ResponseEntity.ok(tasks);
        } catch (Exception e) {
            logger.error("Failed to get user tasks", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get tasks: " + e.getMessage())); // Failed to get tasks
        }
    }

    /**
     * Get tasks with a specific status assigned to the current user
     */
    @GetMapping("/tasks/status/{status}")
    @RequireRole("USER")
    public ResponseEntity<?> getMyTasksByStatus(
            @PathVariable String status,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view tasks with a specific status, User ID: {}, Status: {}", user.getId(), status);

            // Get the tasks with a specific status assigned to the current user
            List<TaskDTO> tasks = taskService.getTasksByAssigneeAndStatus(user.getId(), status);
            return ResponseEntity.ok(tasks);
        } catch (Exception e) {
            logger.error("Failed to get user tasks with a specific status", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get tasks: " + e.getMessage())); // Failed to get tasks
        }
    }

    /**
     * Get task details
     */
    @GetMapping("/tasks/{id}")
    @RequireRole("USER")
    public ResponseEntity<?> getTaskDetail(
            @PathVariable Long id,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                logger.error("Failed to get task details: User does not exist, Username: {}, Task ID: {}", username, id);
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view task details, User ID: {}, Username: {}, Task ID: {}", user.getId(), username, id);

            // Get task details
            TaskDTO task;
            try {
                task = taskService.getTaskById(id);
                logger.info("Task retrieval successful, Task ID: {}, Task Title: {}, Assigned to User ID: {}, Supervisor ID: {}, Current User ID: {}",
                        id, task.getTitle(), task.getAssigneeId(), task.getSupervisorId(), user.getId());
            } catch (Exception e) {
                logger.error("Failed to get task details: Task does not exist or access denied, Task ID: {}", id);
                return ResponseEntity.badRequest().body(Map.of("message", "Task does not exist or access denied")); // Task does not exist or access denied
            }

            // Verify task access permissions
            // Allow three cases: 1. User is the task's assignee 2. assigneeId is null and user is the task's supervisor 3. User is the task's supervisor
            boolean hasAccess = false;
            if (task.getAssigneeId() != null && user.getId().equals(task.getAssigneeId())) {
                // User is the task's assignee
                hasAccess = true;
                logger.info("User is the task's assigned student, access allowed, User ID: {}, Task ID: {}", user.getId(), id);
            } else if (task.getAssigneeId() == null && user.getId().equals(task.getSupervisorId())) {
                // assigneeId is null and user is the task's supervisor
                hasAccess = true;
                logger.info("Task is not assigned to any student, but the user is the task's supervisor, access allowed, User ID: {}, Task ID: {}", user.getId(), id);
            } else if (user.getId().equals(task.getSupervisorId())) {
                // User is the task's supervisor
                hasAccess = true;
                logger.info("User is the task's supervisor, access allowed, User ID: {}, Task ID: {}", user.getId(), id);
            }

            if (!hasAccess) {
                logger.error("Failed to get task details: No permission to view this task, Task ID: {}, Task assigned to User ID: {}, Task Supervisor ID: {}, Current User ID: {}",
                        id, task.getAssigneeId(), task.getSupervisorId(), user.getId());
                return ResponseEntity.badRequest().body(Map.of("message", "No permission to view this task")); // No permission to view this task
            }

            logger.info("User successfully retrieved task details, User ID: {}, Task ID: {}", user.getId(), id);
            return ResponseEntity.ok(task);
        } catch (Exception e) {
            logger.error("Failed to get task details, Task ID: {}, Error: {}", id, e.getMessage(), e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get task details: " + e.getMessage())); // Failed to get task details
        }
    }

    /**
     * Update task status
     */
    @PutMapping("/tasks/{id}/status")
    @RequireRole("USER")
    public ResponseEntity<?> updateTaskStatus(
            @PathVariable Long id,
            @RequestBody Map<String, String> statusData,
            @RequestHeader("Authorization") String auth) {
        String status = statusData.get("status");

        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to update task status, User ID: {}, Task ID: {}, New status: {}", user.getId(), id, status);

            // Get task details
            TaskDTO task = taskService.getTaskById(id);

            // Verify if the task is assigned to the current user
            if (!user.getId().equals(task.getAssigneeId())) {
                return ResponseEntity.badRequest().body(Map.of("message", "No permission to update this task")); // No permission to update this task
            }

            // Update task status
            TaskDTO updatedTask = taskService.updateTaskStatus(id, status);
            return ResponseEntity.ok(updatedTask);
        } catch (Exception e) {
            logger.error("Failed to update task status", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to update task status: " + e.getMessage())); // Failed to update task status
        }
    }

    // ==================== Project-related interfaces ====================

    /**
     * Get all projects assigned to the current user
     */
    @GetMapping("/projects")
    @RequireRole("USER")
    public ResponseEntity<?> getMyProjects(@RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view their own projects, User ID: {}", user.getId());

            // Get the projects assigned to the current user
            List<ProjectDTO> projects = projectService.getProjectsByAssignee(user.getId());
            return ResponseEntity.ok(projects);
        } catch (Exception e) {
            logger.error("Failed to get user projects", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get projects: " + e.getMessage())); // Failed to get projects
        }
    }

    /**
     * Get projects with a specific status assigned to the current user
     */
    @GetMapping("/projects/status/{status}")
    @RequireRole("USER")
    public ResponseEntity<?> getMyProjectsByStatus(
            @PathVariable String status,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view projects with a specific status, User ID: {}, Status: {}", user.getId(), status);

            // Get the projects with a specific status assigned to the current user
            List<ProjectDTO> projects = projectService.getProjectsByAssigneeAndStatus(user.getId(), status);
            return ResponseEntity.ok(projects);
        } catch (Exception e) {
            logger.error("Failed to get user projects with a specific status", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get projects: " + e.getMessage())); // Failed to get projects
        }
    }

    /**
     * Get project details
     */
    @GetMapping("/projects/{id}")
    @RequireRole("USER")
    public ResponseEntity<?> getProjectDetail(
            @PathVariable Long id,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view project details, User ID: {}, Project ID: {}", user.getId(), id);

            // Get project details
            ProjectDTO project = projectService.getProjectById(id);

            // Verify if the project is assigned to the current user
            if (!user.getId().equals(project.getAssigneeId())) {
                return ResponseEntity.badRequest().body(Map.of("message", "No permission to view this project")); // No permission to view this project
            }

            return ResponseEntity.ok(project);
        } catch (Exception e) {
            logger.error("Failed to get project details", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get project details: " + e.getMessage())); // Failed to get project details
        }
    }

    /**
     * Update project status
     */
    @PutMapping("/projects/{id}/status")
    @RequireRole("USER")
    public ResponseEntity<?> updateProjectStatus(
            @PathVariable Long id,
            @RequestBody Map<String, String> statusData,
            @RequestHeader("Authorization") String auth) {
        String status = statusData.get("status");

        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to update project status, User ID: {}, Project ID: {}, New status: {}", user.getId(), id, status);

            // Get project details
            ProjectDTO project = projectService.getProjectById(id);

            // Verify if the project is assigned to the current user
            if (!user.getId().equals(project.getAssigneeId())) {
                return ResponseEntity.badRequest().body(Map.of("message", "No permission to update this project")); // No permission to update this project
            }

            // Update project status
            ProjectDTO updatedProject = projectService.updateProjectStatus(id, status);
            return ResponseEntity.ok(updatedProject);
        } catch (Exception e) {
            logger.error("Failed to update project status", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to update project status: " + e.getMessage())); // Failed to update project status
        }
    }

    /**
     * Student apply for a project
     */
    @PostMapping("/projects/apply")
    @RequireRole("USER")
    public ResponseEntity<?> applyForProject(
            @RequestBody ProjectDTO projectDTO,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User applied for a project, User ID: {}, Project Title: {}", user.getId(), projectDTO.getTitle());

            // Set the applicant ID
            projectDTO.setAssigneeId(user.getId());

            // Apply for the project
            ProjectDTO appliedProject = projectService.applyForProject(projectDTO);
            return ResponseEntity.ok(appliedProject);
        } catch (Exception e) {
            logger.error("Failed to apply for project", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to apply for project: " + e.getMessage())); // Failed to apply for project
        }
    }

    /**
     * Get all supervisors
     */
    @GetMapping("/supervisors")
    @RequireRole({"ADMIN", "SUPERVISOR", "USER"})
    public ResponseEntity<?> getAllSupervisors() {
        try {
            List<User> supervisors = userService.getAllSupervisors();
            return ResponseEntity.ok(supervisors);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get supervisor list: " + e.getMessage())); // Failed to get supervisor list
        }
    }

    /**
     * Get all tasks under a project
     */
    @GetMapping("/projects/{id}/tasks")
    @RequireRole("USER")
    public ResponseEntity<?> getProjectTasks(
            @PathVariable Long id,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to view project-related tasks, User ID: {}, Project ID: {}", user.getId(), id);

            // Get project details to verify permissions
            ProjectDTO project = projectService.getProjectById(id);

            // Check if the user is a supervisor role
            boolean isSupervisor = user.getRoles().stream()
                    .anyMatch(role -> role.getName().equalsIgnoreCase("SUPERVISOR") ||
                            role.getName().equalsIgnoreCase("ADMIN"));

            // Verify if the project belongs to the current user; skip this check if the user is a supervisor
            if (!isSupervisor && !user.getId().equals(project.getAssigneeId())) {
                logger.warn("Permission error: User {} tried to access tasks of project {} that does not belong to them", user.getId(), id);
                return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("message", "No permission to view tasks of this project")); // No permission to view tasks of this project
            }

            // Get the task list under the project
            List<TaskDTO> tasks = taskService.getBatchTasksByProject(id);
            return ResponseEntity.ok(tasks);
        } catch (Exception e) {
            logger.error("Failed to get project tasks", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to get project tasks: " + e.getMessage())); // Failed to get project tasks
        }
    }

    /**
     * Submit project for review
     */
    @PutMapping("/projects/{id}/submit-for-review")
    @RequireRole("USER")
    public ResponseEntity<?> submitProjectForReview(
            @PathVariable Long id,
            @RequestHeader("Authorization") String auth) {
        try {
            // Get the current user
            String token = auth.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);
            User user = userService.findByUsername(username);

            if (user == null) {
                return ResponseEntity.badRequest().body(Map.of("message", "User does not exist"));
            }

            logger.info("User requested to submit project for review, User ID: {}, Project ID: {}", user.getId(), id);

            // Get project details
            ProjectDTO project = projectService.getProjectById(id);

            // Verify if the project is assigned to the current user
            if (!user.getId().equals(project.getAssigneeId())) {
                return ResponseEntity.badRequest().body(Map.of("message", "No permission to submit this project for review"));
            }

            // Update project status to "Waiting for Review"
            ProjectDTO updatedProject = projectService.updateProjectStatus(id, "Waiting for Review");
            
            logger.info("Project successfully submitted for review, Project ID: {}", id);
            return ResponseEntity.ok(updatedProject);
        } catch (Exception e) {
            logger.error("Failed to submit project for review", e);
            return ResponseEntity.badRequest().body(Map.of("message", "Failed to submit project for review: " + e.getMessage()));
        }
    }
}